use std::net::{SocketAddr, TcpListener};
use std::time::Duration;

use pdata::internal::data::protogen::collector::logs::v1::{ExportLogsServiceRequest, logs_service_server::{LogsService, LogsServiceServer}, ExportLogsServiceResponse};
use pdata::internal::data::protogen::collector::metrics::v1::{ExportMetricsServiceRequest, metrics_service_server::{MetricsService, MetricsServiceServer}, ExportMetricsServiceResponse};
use pdata::internal::data::protogen::collector::trace::v1::{ExportTraceServiceRequest, trace_service_server::{TraceService, TraceServiceServer}, ExportTraceServiceResponse};
use rust_collector::exporter::otlp_grpc_exporter::OtlpGrpcExporter;
use tonic::{Request, Response, Status};

fn pick_free_port() -> u16 {
    let l = TcpListener::bind("127.0.0.1:0").expect("bind temp");
    let port = l.local_addr().unwrap().port();
    drop(l);
    port
}

#[derive(Default)]
struct TracesSvc;

#[tonic::async_trait]
impl TraceService for TracesSvc {
    async fn export(&self, _req: Request<ExportTraceServiceRequest>) -> Result<Response<ExportTraceServiceResponse>, Status> {
        Ok(Response::new(ExportTraceServiceResponse { partial_success: None }))
    }
}

#[derive(Default)]
struct MetricsSvc;

#[tonic::async_trait]
impl MetricsService for MetricsSvc {
    async fn export(&self, _req: Request<ExportMetricsServiceRequest>) -> Result<Response<ExportMetricsServiceResponse>, Status> {
        Ok(Response::new(ExportMetricsServiceResponse { partial_success: None }))
    }
}

#[derive(Default)]
struct LogsSvc;

#[tonic::async_trait]
impl LogsService for LogsSvc {
    async fn export(&self, _req: Request<ExportLogsServiceRequest>) -> Result<Response<ExportLogsServiceResponse>, Status> {
        Ok(Response::new(ExportLogsServiceResponse { partial_success: None }))
    }
}

#[tokio::test]
async fn grpc_exporter_sends_to_server() {
    let addr: SocketAddr = format!("127.0.0.1:{}", pick_free_port()).parse().unwrap();

    tokio::spawn(async move {
        tonic::transport::Server::builder()
            .add_service(TraceServiceServer::new(TracesSvc::default()))
            .add_service(MetricsServiceServer::new(MetricsSvc::default()))
            .add_service(LogsServiceServer::new(LogsSvc::default()))
            .serve(addr)
            .await
            .unwrap();
    });

    tokio::time::sleep(Duration::from_millis(100)).await;

    let exp = OtlpGrpcExporter::connect(format!("http://{}", addr)).await.unwrap();

    <OtlpGrpcExporter as rust_collector::consumer::ConsumeTraces>::consume(&*exp, ExportTraceServiceRequest::default()).await;
    <OtlpGrpcExporter as rust_collector::consumer::ConsumeMetrics>::consume(&*exp, ExportMetricsServiceRequest::default()).await;
    <OtlpGrpcExporter as rust_collector::consumer::ConsumeLogs>::consume(&*exp, ExportLogsServiceRequest::default()).await;
}


